/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) KeyBit.java 2018-08-06 17:26
 */

package cn.jh.common.core.crypto;

import com.google.common.primitives.Ints;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.beans.PropertyEditor;
import java.beans.PropertyEditorSupport;


/**
 * 密钥位长度枚举。
 *
 * @author Fuchun
 * @since 1.0
 */
public enum KeyBit {

    /**
     * 密钥大小：128位，数据长度：16。
     */
    KB_128(128, "keyBit.length.128", "128位"),

    /**
     * 密钥大小：168位，数据长度：24
     */
    KB_168(168, "keyBit.length.168", "168位"),

    /**
     * 密钥大小：192位，数据长度：24。
     */
    KB_192(192, "keyBit.length.192", "192位"),

    /**
     * 密钥大小：256位，数据长度：32。
     */
    KB_256(256, "keyBit.length.256", "256位"),

    /**
     * 密钥大小：384位，数据长度：40。
     */
    KB_284(384, "keyBit.length.384", "384位"),

    /**
     * 密钥大小：512位，数据长度：64。
     */
    KB_512(512, "keyBit.length.512", "512位"),

    /**
     * 密钥大小：1024位，数据长度：128。
     */
    KB_1024(1024, "keyBit.length.1024", "1024位"),

    /**
     * 密钥大小：2048位，数据长度：256。
     */
    KB_2048(2048, "keyBit.length.2048", "2048位")
    ;

    public static PropertyEditor propertyEditor() {
        return new StringToKeyBitEditor();
    }

    final int bit;
    final int keyLength;
    final String code;
    final String defaultText;

    KeyBit(int bit, String code, String defaultText) {
        this.bit = bit;
        this.keyLength = this.bit / 8;
        this.code = code;
        this.defaultText = defaultText;
    }

    public int getBit() {
        return bit;
    }

    public int getKeyLength() {
        return keyLength;
    }

    public String getCode() {
        return code;
    }

    public String getDefaultText() {
        return defaultText;
    }

    /**
     * Return the {@code KeyBit} of the given bits. {@link #KB_256} will be returned
     * if the {@code n} not exists.
     *
     * @param n the key bits.
     * @param <N> the key bits type.
     * @return the {@code KeyBit} of the given bits.
     */
    @NotNull
    public static <N extends Number> KeyBit of(N n) {
        return of(n, KB_256);
    }

    /**
     * Return the {@code KeyBit} of the given bits. Otherwise return {@code def}
     * if the {@code n} not exists.
     *
     * @param n the key bits.
     * @param def the given default {@code KeyBit}.
     * @param <N> the key bits type.
     * @return the {@code KeyBit} of the given bits.
     */
    @Contract("_, !null -> !null")
    public static <N extends Number> KeyBit of(N n, KeyBit def) {
        if (n == null) {
            return def;
        }
        final int bit = n.intValue();
        for (KeyBit keyBit : values()) {
            if (keyBit.bit == bit) {
                return keyBit;
            }
        }
        return def;
    }

    /**
     * Return the {@code KeyBit} of the given name. Otherwise {@code null}.
     *
     * @param name the {@code KeyBit} name.
     * @return the {@code KeyBit} of the given name.
     */
    @Nullable
    public static KeyBit of(String name) {
        return of(name, null);
    }

    /**
     * Return the {@code KeyBit} of the given name. Otherwise return {@code def}
     * if the {@code name} not exists.
     *
     * @param name the {@code KeyBit} name.
     * @param def the given default {@code KeyBit}.
     * @return the {@code KeyBit} of the given name.
     */
    @Contract("_, !null -> !null")
    public static KeyBit of(String name, KeyBit def) {
        if (name == null || name.isEmpty()) {
            return def;
        }
        String n = name.toUpperCase();
        for (KeyBit keyBit : values()) {
            if (keyBit.name().equals(n)) {
                return keyBit;
            }
        }
        return def;
    }

    /**
     * Return {@code true} if the {@code KeyBit} enums has the given bits, otherwise {@code false}.
     *
     * @param bits the key bits.
     */
    public static boolean contains(int bits) {
        if (bits < KB_128.bit || bits > KB_2048.bit) {
            return false;
        }
        for (KeyBit kb : values()) {
            if (kb.bit == bits) {
                return true;
            }
        }
        return false;
    }

    private static class StringToKeyBitEditor extends PropertyEditorSupport {
        @Override
        public void setAsText(String text) throws IllegalArgumentException {
            if (text == null || text.isEmpty()) {
                throw new IllegalArgumentException("KeyBit value must be not null");
            }
            KeyBit keyBit = null;
            try {
                keyBit = KeyBit.valueOf(text);
            } catch (IllegalArgumentException ex) {
                // try number
                Integer n = Ints.tryParse(text);
                if (n != null) {
                    keyBit = of(n, null);
                }
            }
            super.setValue(keyBit);
        }
    }

}
